let currentBlock; //当前方块
let blockQueue = []; //方块队列
let containerBlock; //容器
let game; //当前游戏
let audio //背景音乐
let score //分数
/**
 * 随机获取方块
 * @returns block
 */
function getRandomBlock() {
    let random = Math.floor(Math.random() * 7);
    return {
        block: Block[random],
        color: BlockColor[random],
        position: {
            x: 3,
            y: -3
        }
    }
}

/**
 * 获取并刷新方块队列
 * @returns block
 */
function getBlockFromQueue() {
    blockQueue.push(getRandomBlock())
    return blockQueue.shift()
}

/**
 * @description 清除旧块，绘制新块
 * @param  {getRandomBlock} oldBlock 
 * @param {getRandomBlock} newBlock 
 */
function drawBlock(oldBlock, newBlock) {
    let canvas = $('#container').get(0);
    let ctx = canvas.getContext('2d');
    //清除旧方块
    ctx.save()
    ctx.translate(oldBlock.position.x * blockSize, oldBlock.position.y * blockSize)
    for (let i in oldBlock.block) {
        for (let j in oldBlock.block[i]) {
            if (oldBlock.block[i][j] == 1) {
                ctx.clearRect(j * blockSize, i * blockSize, blockSize, blockSize);
            }
        }
    }
    ctx.restore()
    //绘制新的方块
    ctx.save()
    ctx.translate(newBlock.position.x * blockSize, newBlock.position.y * blockSize)
    ctx.fillStyle = newBlock.color;
    for (let i in newBlock.block) {
        for (let j in newBlock.block[i]) {
            if (newBlock.block[i][j] == 1) {
                ctx.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
            }
        }
    }
    ctx.restore()
}

/**
 * 方块定时下落
 */
function autoFallDown() {
    game = window.setInterval(function () {
        let newBlock = JSON.parse(JSON.stringify(currentBlock))
        newBlock.position.y++
        if (canMove(newBlock)) {
            drawBlock(currentBlock, newBlock)
            currentBlock = newBlock
        } else {
            recordBlock(currentBlock)
        }
    }, fallDownSpeed)
}

/**
 * 监听键盘操作
 */
function listenKeyBoard() {
    $(document).keydown(function (e) {
        let newBlock = JSON.parse(JSON.stringify(currentBlock))
        switch (e.keyCode) {
            case 32:
                //space键硬降
                fallDownBottom(newBlock)
                break
            case 74:
                //J键左转
                rotateBlock(newBlock, 'left')
                moveOrRecord(newBlock, false);
                break;
            case 75:
                //K键右转
                rotateBlock(newBlock, 'right')
                moveOrRecord(newBlock, false);
                break;
            case 76:
                //L键180°
                rotateBlock(newBlock, 'overturn')
                moveOrRecord(newBlock, false);
                break;
            case 83:
                //S键软降
                newBlock.position.y++;
                moveOrRecord(newBlock, true);
                break;
            case 65:
                //A键左移
                newBlock.position.x--;
                moveOrRecord(newBlock, false);
                break;
            case 68:
                //D键右移
                newBlock.position.x++;
                moveOrRecord(newBlock, false);
                break;
        }
    });
}

function moveOrRecord(newBlock, isRecord) {
    if (canMove(newBlock)) {
        drawBlock(currentBlock, newBlock);
        currentBlock = newBlock;
    } else if (isRecord) {
        recordBlock(currentBlock);
    }
}

/**
 * 方块硬降
 * @param {getRandomBlock} block 
 */
function fallDownBottom(block) {
    let len = block.block.length,
        x = block.position.x,
        y = block.position.y,
        distance = Number.MAX_VALUE;
    loop:
        for (let i = x; i < x + len; i++) {
            let tempDistance = 0,
                flag = y;
            for (let j = len - 1; j >= -1; j--) {
                if (j == -1) {
                    continue loop;
                } else if (block.block[j][i - x] == 1) {
                    flag = y + j + 1;
                    break;
                }
            }
            for (let j = flag; j < cHeight; j++) {
                if (containerBlock[j] && containerBlock[j][i] == 1) {
                    break;
                }
                tempDistance++;
            }
            distance = Math.min(tempDistance, distance);
        }
    block.position.y += distance
    drawBlock(currentBlock, block)
    recordBlock(block)
}

/**
 * 方块旋转操作
 * @param {getRandomBlock} block 
 * @param {string} direction "right"右旋(顺时针) "left"左旋(顺时针) "overturn"180°旋转
 */
function rotateBlock(block, direction) {
    let len = block.block.length;
    let last = len - 1
    let temp = Array.from({
        length: len
    }).map(() => Array.from({
        length: len
    }).fill(0))
    switch (direction) {
        case 'right':
            for (var i = 0; i < len; i++) {
                var k = last - i;
                for (var j = 0; j < len; j++) {
                    temp[j][k] = block.block[i][j];
                }
            }
            break
        case 'left':
            for (var i = 0; i < len; i++) {
                for (var j = 0; j < len; j++) {
                    var k = last - j;
                    temp[k][i] = block.block[i][j];
                }
            }
            break
        case 'overturn':
            for (var i = 0; i < len; i++) {
                var k = last - i;
                for (var j = 0; j < len; j++) {
                    var z = last - j
                    temp[k][z] = block.block[i][j];
                }
            }
            break
    }
    block.block = temp
}


/**
 * 判断方块是否可以移动
 * @param {getRandomBlock} block 
 * @returns boolean
 */
function canMove(block) {
    let len = block.block.length
    let x = block.position.x,
        y = block.position.y;
    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len; j++) {
            let xBlock = x + j,
                yBlock = y + i;
            if (block.block[i][j] == 1) {
                if (xBlock < 0 || xBlock >= cWidth || yBlock >= cHeight) {
                    return false
                }
                if (0 <= yBlock && yBlock < cHeight && 0 <= xBlock && xBlock < cWidth && containerBlock[yBlock][xBlock] == 1) {
                    return false;
                }
            }
        }
    }
    return true
}

/**
 * @description 记录方块
 * @param {方块} block 
 */
function recordBlock(block) {
    let len = block.block.length
    for (let i = len - 1; i >= 0; i--) {
        for (let j = 0; j < len; j++) {
            if (block.block[i][j] == 1) {
                if (block.position.y + i < 0) {
                    gameOver()
                    return;
                }
                containerBlock[block.position.y + i][block.position.x + j] = 1
            }
        }
    }
    eliminateBlock(block)
    currentBlock = getBlockFromQueue()
    showAndRefreshBlockQueue()
}

/**
 * @description 消除操作
 * @param {getRandomBlock} block 
 */
function eliminateBlock(block) {
    let len = block.block.length,
        y = block.position.y;
    for (let i = y + len - 1; i >= y; i--) {
        if (i >= cHeight) {
            continue;
        }
        let point = 0
        for (let x = 0; x < cWidth; x++) {
            if (containerBlock[i][x] == 0) {
                continue;
            }
            point++;
        }
        if (point == cWidth) {
            refreshScore()
            containerBlock.splice(i, 1)
            containerBlock.unshift(Array.from({
                length: cWidth
            }).fill(0))
            let ctx = $('#container').get(0).getContext('2d');
            if (i > 0) {
                ctx.clearRect(0, i * blockSize, cWidthSize, blockSize)
                var blockData = ctx.getImageData(0, 0, cWidthSize, i * blockSize)
                ctx.putImageData(blockData, 0, blockSize);
            } else {
                ctx.clearRect(0, 0, cWidthSize, blockSize)
            }
            i++;
        }
    }
}

/**
 * @description 增加并刷新分数
 */
function refreshScore() {
    score += 10;
    let ctx = $('#menu').get(0).getContext('2d');
    ctx.clearRect(0.5 * cWidthSize, 0, cWidthSize, cHeightSize);
    ctx.fillStyle = "#00e7fe"
    ctx.textAlign = "center";
    ctx.font = "oblique 24px sans-serif";
    ctx.fillText("Score", 0.75 * cWidthSize, 0.35 * cHeightSize);
    ctx.font = "oblique 30px sans-serif";
    ctx.fillText(score.toString(), 0.75 * cWidthSize, 0.5 * cHeightSize);
}

/**
 * @description 游戏结束
 */
function gameOver() {
    window.clearInterval(game);
    audio.parse();
    initGame();
    startGame();
}

/**
 * @description 显示和刷新方块队列
 */
function showAndRefreshBlockQueue() {
    let ctx = $('#menu').get(0).getContext('2d')
    ctx.clearRect(0, 0, 0.5 * cWidthSize, cHeightSize)
    for (let b = 0; b < 5; b++) {
        ctx.save()
        ctx.translate(0.05 * cWidthSize, b * 4 * blockSize + blockSize)
        ctx.fillStyle = blockQueue[b].color;
        for (let i in blockQueue[b].block) {
            for (let j in blockQueue[b].block[i]) {
                if (blockQueue[b].block[i][j] == 1) {
                    ctx.fillRect(j * blockSize, i * blockSize, blockSize, blockSize);
                }
            }
        }
        ctx.restore()
    }
}

/**
 * @description 初始化游戏
 */
function initGame() {
    //初始化游戏区域
    let canvas = $('#container').get(0);
    canvas.width = cWidthSize;
    canvas.height = cHeightSize;
    let ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    //初始化菜单区域
    let menu = $('#menu').get(0);
    menu.width = cWidthSize;
    menu.height = cHeightSize;
    ctx = menu.getContext('2d');
    ctx.clearRect(0, 0, menu.width, menu.height)
    ctx.fillStyle = "#00e7fe"
    ctx.textAlign = "center"
    ctx.font = "oblique 48px sans-serif";
    ctx.fillText("Tetris", cWidth / 2 * blockSize, cHeight / 3 * blockSize)
    ctx.font = "oblique 20px sans-serif";
    ctx.fillText("Keyboard Start", cWidth / 2 * blockSize, cHeight / 5 * 3.5 * blockSize)
    ctx.font = "15px sans-serif";
    ctx.fillText("a,s,d 左移,软降,右移", cWidth / 2 * blockSize, cHeight / 5 * 4.2 * blockSize)
    ctx.fillText("j,k,l 逆转,顺转,180°", cWidth / 2 * blockSize, cHeight / 5 * 4.4 * blockSize)
    //初始化数组
    containerBlock = Array.from({
        length: cHeight
    }).map(() => Array.from({
        length: cWidth
    }).fill(0))
    //消除键盘监听事件
    $(document).off("keydown")
    //初始化方块队列
    for (let i = 0; i < 5; i++) {
        blockQueue[i] = getRandomBlock()
    }
    //初始化当前方块
    currentBlock = getBlockFromQueue();
    //初始化分数
    score = -10;
}

/**
 * @description 监听键盘事件触发开始游戏
 */
function startGame() {
    document.addEventListener('keypress', function () {
        let ctx = $('#menu').get(0).getContext('2d').clearRect(0, 0, cWidthSize, cHeightSize);
        showAndRefreshBlockQueue();
        refreshScore();
        listenKeyBoard();
        autoFallDown();
        //播放音乐
        audio = new Audio("music/super.mp3");
        audio.loop = true;
        audio.play();
    }, {
        once: true
    });
}

/**
 * 页面加载完成时，初始化游戏，并等待开始游戏
 */
$(document).ready(function () {
    initGame();
    startGame();
})